#ifdef WIN32
#include <objbase.h>
#endif

#include "utils.h"
#include <algorithm>
#include <ctime>
#include <uuid.h>
using namespace std;
using namespace uuids;

onvifToolUtils::onvifToolUtils()
{
    logLevel_ = -1;
    fileName_ = "";
}

void onvifToolUtils::setLogToFile(int logLevel, char const *strLogDir, int autoDel)
{
    // 保存信息
    mutex_.lock();
    logLevel_ = logLevel;
    logDir = strLogDir ? string(strLogDir) : string(".");
    mutex_.unlock();
}

// 指定log保存到指定文件，fileName为nullptr时使用默认生成的文件，strLogDir 为nullptr时，相对路径为setLogToFile设置的路径
void onvifToolUtils::setLogFileName(char const *fileName, char const *strLogDir)
{
    mutex_.lock();
    if (fileName == nullptr)
    {
        fileName_ = string("");
        mutex_.unlock();
        return;
    }
    if (strLogDir == nullptr)
    {
        fileName_ = logDir == "" ? string(fileName) : logDir + "/" + string(fileName);
    }
    else
    {
        fileName_ = string(strLogDir) + string("/") + string(fileName);
    }
    mutex_.unlock();
}

void onvifToolUtils::log(string message, int logLevel)
{
    mutex_.lock();
    // 当前使用的日志级别
    int curLogLevel = logLevel;

    string fileName;
    string date = getDate();
    string time_ = getTime();

    // 附加上时间和日志级别的新消息
    string newMessage;

    switch (curLogLevel)
    {
    case -1:
        // 控制台输出
        newMessage = string("[") + time_ + "]: \t" + message + "\n";
        printf("%s", newMessage.c_str());
        break;
    case 0:
        // debug
        newMessage = string("[") + time_ + " _debug_ ]: \t" + message + "\n";
        break;
    case 1:
        // info
        newMessage = string("[") + time_ + " _info_ ]: \t" + message + "\n";
        break;
    case 2:
        // warning
        newMessage = string("[") + time_ + " _warning_ ]: \t" + message + "\n";
        break;
    case 3:
        // error
        newMessage = string("[") + time_ + " _error_ ]: \t" + message + "\n";
        break;
    case 4:
        // critical
        newMessage = string("[") + time_ + " _critical_ ]: \t" + message + "\n";
        break;

    default:
        break;
    }

    if (curLogLevel == -1)
    {
        mutex_.unlock();
        return;
    }

    // 判断该日志级别信息是否需要写入文件，级别高的（严重程度高的）需要写入
    if (logLevel < logLevel_)
    {
        mutex_.unlock();
        return;
    }

    // 如果未指定文件，使用自动生成的文件，否则使用指定文件
    if (fileName_ == "")
    {
        fileName = logDir + "/ONVIF_LOG_" + date + ".log";
    }
    else
    {
        fileName = fileName_;
    }

    logFile = fopen(fileName.c_str(), "a");
    fwrite(newMessage.c_str(), newMessage.size(), 1, logFile);
    fclose(logFile);
    mutex_.unlock();
}

// 获取日期y_m_d
string onvifToolUtils::getDate()
{
    time_t nowTime;
    time(&nowTime);
    auto p = localtime(&nowTime);
    int year = p->tm_year + 1900;
    int month = p->tm_mon + 1;
    int day = p->tm_mday;

    string date;
    char dateStr[15];
    sprintf(dateStr, "%04d_%02d_%02d", year, month, day);
    date = string(dateStr);
    return date;
}

// 获取时间h:m:s
string onvifToolUtils::getTime()
{
    time_t nowTime;
    time(&nowTime);
    auto p = localtime(&nowTime);
    int hour = p->tm_hour;
    int minute = p->tm_min;
    int second = p->tm_sec;

    string time_;
    char timeStr[10];
    sprintf(timeStr, "%02d:%02d:%02d", hour, minute, second);
    time_ = string(timeStr);
    return time_;
}

template <typename EngineT, std::size_t StateSize = EngineT::state_size>
inline void seed_rng(EngineT &engine) {
  using engine_type = typename EngineT::result_type;
  using device_type = std::random_device::result_type;
  using seedseq_type = std::seed_seq::result_type;
  constexpr auto bytes_needed = StateSize * sizeof(engine_type);
  constexpr auto numbers_needed = (sizeof(device_type) < sizeof(seedseq_type))
                                      ? (bytes_needed / sizeof(device_type))
                                      : (bytes_needed / sizeof(seedseq_type));
  std::array<device_type, numbers_needed> numbers{};
  std::random_device rnddev{};
  std::generate(std::begin(numbers), std::end(numbers), std::ref(rnddev));
  std::seed_seq seedseq(std::cbegin(numbers), std::cend(numbers));
  engine.seed(seedseq);
}

string onvifToolUtils::generateUUID()
{
#ifdef WIN32
    char buffer[64] = {0};
    GUID guid;
    CoCreateGuid(&guid);
    _snprintf(buffer, sizeof(buffer),
              "%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
              guid.Data1, guid.Data2, guid.Data3,
              guid.Data4[0], guid.Data4[1], guid.Data4[2],
              guid.Data4[3], guid.Data4[4], guid.Data4[5],
              guid.Data4[6], guid.Data4[7]);
    string uuidStr(buffer);
#else
  auto engine = uuids::uuid_random_generator::engine_type{};
  seed_rng(engine);
  uuids::uuid_random_generator gen{engine};
  auto id = gen();
  return uuids::to_string(id);
#endif
}

string onvifToolUtils::prefixToSubmask(int len)
{
    if (len > 32 || len <= 0)
    {
        return string("");
    }
    int a = len / 8;
    int b = len % 8;

    if (a == 4)
    {
        return string("255.255.255.255");
    }

    string result = "";
    for (int i = 0; i < a; i++)
    {
        result += "255.";
    }

    unsigned int c = 1 << (8 - b);
    result += to_string(256 - c);

    return result;
}

string onvifToolUtils::toLower(string str)
{
    string newStr = str;
    transform(newStr.begin(), newStr.end(), newStr.begin(), ::tolower);
    return newStr;
}

bool onvifToolUtils::compareServiceName(string serviceNameFind, string serviceName)
{
    // 提取服务名并转成小写
    auto index = serviceNameFind.find_last_of("_");
    string newServiceNameFind;
    if (index >= 0 && index < serviceNameFind.size())
    {
        newServiceNameFind = serviceNameFind.substr(0, index);
        newServiceNameFind = toLower(newServiceNameFind);
    }
    else
    {
        newServiceNameFind = serviceNameFind;
        newServiceNameFind = toLower(newServiceNameFind);
    }

    // 提取服务名并转成小写
    index = serviceName.find_last_of("_");
    string newServiceName;
    if (index >= 0 && index < serviceName.size())
    {
        newServiceName = serviceName.substr(0, index);
        newServiceName = toLower(newServiceName);
    }
    else
    {
        newServiceName = serviceName;
        newServiceName = toLower(newServiceName);
    }

    // 比较字符串
    if (newServiceName == newServiceNameFind)
    {
        return true;
    }
    else if (newServiceNameFind != "media" && newServiceName.find(newServiceNameFind) < newServiceName.size())
    {
        return true;
    }
    return false;
}

// dateTime格式: "2023-12-27T11:42:30Z"
string onvifToolUtils::time_tToDateTime(time_t timeVal)
{
    struct tm *timePtr;
    timePtr = gmtime(&timeVal);

    int year = timePtr->tm_year + 1900;
    int month = timePtr->tm_mon + 1;
    int day = timePtr->tm_mday;

    int hour = timePtr->tm_hour;
    int minute = timePtr->tm_min;
    int second = timePtr->tm_sec;

    string time_;
    char timeStr[25] = {0};
    sprintf(timeStr, "%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second);
    time_ = string(timeStr);
    return time_;
}

// dateTime格式: "2023-12-27T11:42:30Z"
time_t onvifToolUtils::dateTimeToTime_t(string dateTimeVal)
{
    int year = stoi(dateTimeVal.substr(0, 4)) - 1900;
    int month = stoi(dateTimeVal.substr(5, 2)) - 1;
    int day = stoi(dateTimeVal.substr(8, 2));

    int hour = stoi(dateTimeVal.substr(11, 2));
    int minute = stoi(dateTimeVal.substr(14, 2));
    int second = stoi(dateTimeVal.substr(17, 2));

    struct tm tm_time = {0};
    tm_time.tm_year = year;
    tm_time.tm_mon = month;
    tm_time.tm_mday = day;
    tm_time.tm_hour = hour;
    tm_time.tm_min = minute;
    tm_time.tm_sec = second;
    tm_time.tm_isdst = 0;

    // 得到的结果会被转成本地时间，需改成UTC
    // 例：传入utc时间是0点整，对应的北京时间是8点整，然而传入的utc时间被当成了北京时间0点整，
    // 此时得到的时间戳相比正确的时间戳慢了8个小时，需要把慢了的8个小时加回来
    time_t localTime_ = mktime(&tm_time);

    // 计算UTC与本地时间的时差
    time_t nowTime;
    time(&nowTime);
    struct tm *utcTime, *locTime;
    utcTime = gmtime(&nowTime);
    time_t utcTime_t = mktime(utcTime);

    locTime = localtime(&nowTime);
    time_t locTime_t = mktime(locTime);

    // 得到差值（负数，utc时间比本地时间慢，返回值需加上慢了的时间，正数，utc时间比本地时间快，返回值需减去快了的时间）
    auto diff = utcTime_t - locTime_t;

    return localTime_ - diff;
}

// localTime格式: "2023-12-27 11:42:30" 本地时间
string onvifToolUtils::time_tToLocalTime(time_t timeVal)
{
    struct tm *timePtr;
    timePtr = localtime(&timeVal);

    int year = timePtr->tm_year + 1900;
    int month = timePtr->tm_mon + 1;
    int day = timePtr->tm_mday;

    int hour = timePtr->tm_hour;
    int minute = timePtr->tm_min;
    int second = timePtr->tm_sec;

    string time_;
    char timeStr[25] = {0};
    sprintf(timeStr, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
    time_ = string(timeStr);
    return time_;
}

// localTime格式: "2023-12-27 11:42:30" 本地时间
time_t onvifToolUtils::localTimeToTime_t(string localTimeVal)
{
    int year = stoi(localTimeVal.substr(0, 4)) - 1900;
    int month = stoi(localTimeVal.substr(5, 2)) - 1;
    int day = stoi(localTimeVal.substr(8, 2));

    int hour = stoi(localTimeVal.substr(11, 2));
    int minute = stoi(localTimeVal.substr(14, 2));
    int second = stoi(localTimeVal.substr(17, 2));

    struct tm tm_time = {0};
    tm_time.tm_year = year;
    tm_time.tm_mon = month;
    tm_time.tm_mday = day;
    tm_time.tm_hour = hour;
    tm_time.tm_min = minute;
    tm_time.tm_sec = second;
    tm_time.tm_isdst = 0;

    // 得到的结果会被转成本地时间
    time_t localTime_ = mktime(&tm_time);

    return localTime_;
}
